// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


`define SCM_GLOBAL_MAX_ADDR  8 // define how many DW registers are used
//Define Address for each regiter
`define SCM_GLB_FPGA_VERSION_ADDR       16'h00
`define SCM_GLB_REV_CTRL_ADDR           16'h04
`define SCM_GLB_BUILD_TIME_ADDR         16'h08
`define SCM_GLB_BUILD_CATEGORY_ADDR     16'h0C
`define SCM_GLB_DEVICE_LOCATION_ADDR    16'h10
`define SCM_GLB_STATUS_ADDR             16'h14
`define SCM_GLB_CONTROL_ADDR            16'h18
`define SCM_GLB_SCRATCH_ADDR            16'h1C


//Define Default Value
`define SCM_GLB_MAJOR_RELEASE_NUM_DEF  16'h2
`define SCM_GLB_MINOR_RELEASE_NUM_DEF  16'h2

`define SCM_GLB_GIT_HASH_DEF           32'h0

`define SCM_GLB_BUILD_YEAR_DEF         04'h1
`define SCM_GLB_BUILD_MONTH_DEF        04'h6
`define SCM_GLB_BUILD_DAY_DEF          08'h1
`define SCM_GLB_BUILD_HOUR_DEF         08'h0
`define SCM_GLB_BUILD_MINUTE_DEF       08'h0

`define SCM_GLB_BUILD_CATEGORY_DEF     01'h0

`define SCM_GLB_DEVICE_LOCTAION_DEF    02'h1

`define SCM_GLB_UART_SRC_SEL_DEF       02'h1


module scm_global_csr 
(
    input clk,
    input reset,

    //avmm interface
    input      [31:0] avmm_address,
    input             avmm_write,
    input      [31:0] avmm_writedata,          
    input             avmm_read,     
    output reg [31:0] avmm_readdata,      
    output reg        avmm_readdatavalid, 
    output            avmm_waitrequest,           
    input      [ 3:0] avmm_byteenable,

    //misc signals
    input             mcsi_aligned,
    output            uart_source_sel, //Default select UART from CPU1
    output            bmc_force_update_sel,
	 
	 //error report
	 output            invalid_access
);

wire [15:0] addr_local; //Only 16 bits out of 32 bits avmm_address supported
reg  [31:0] scm_global_reg [`SCM_GLOBAL_MAX_ADDR-1:0]; //local register
reg  [31:0] illegal_write;

reg         invalid_read;
reg         invalid_write;

assign avmm_waitrequest = 1'b0;

assign addr_local = avmm_address[15:0];

assign invalid_access   =  invalid_write || invalid_read;

//invalid_read, using combinational logic to report error
always @ ( * ) begin
    if ( reset ) begin
		  invalid_read       = 1'b0;
    end else begin
        if ( avmm_read && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                 `SCM_GLB_FPGA_VERSION_ADDR    : begin
						  invalid_read           =   1'b0;
                 end
                 `SCM_GLB_REV_CTRL_ADDR        : begin
						  invalid_read           =   1'b0;
                 end
                 `SCM_GLB_BUILD_TIME_ADDR      : begin
                    invalid_read           =   1'b0;
                 end
                 `SCM_GLB_BUILD_CATEGORY_ADDR  : begin
						  invalid_read           =   1'b0;
                 end
                 `SCM_GLB_DEVICE_LOCATION_ADDR : begin
						  invalid_read           =   1'b0;
                 end
                 `SCM_GLB_STATUS_ADDR          : begin
						  invalid_read           =   1'b0;
                 end
                 `SCM_GLB_CONTROL_ADDR         : begin
						  invalid_read           =   1'b0;
                 end
                 `SCM_GLB_SCRATCH_ADDR         : begin
					     invalid_read           =   1'b0;
                 end	  
                 default                : begin
						  invalid_read           =   1'b1;
                 end
            endcase
        end
		  else begin
			  invalid_read                    =   1'b0;
		  end
    end
end

//read registers
always @ ( posedge clk ) begin
    if ( reset ) begin
        avmm_readdata     <= 32'h0;
    end else begin
        if ( avmm_read && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                 `SCM_GLB_FPGA_VERSION_ADDR    : begin
					     avmm_readdata         <= {`SCM_GLB_MAJOR_RELEASE_NUM_DEF, `SCM_GLB_MINOR_RELEASE_NUM_DEF};
                 end
                 `SCM_GLB_REV_CTRL_ADDR        : begin
					     avmm_readdata         <= `SCM_GLB_GIT_HASH_DEF;
                 end
                 `SCM_GLB_BUILD_TIME_ADDR      : begin
					     avmm_readdata         <= {`SCM_GLB_BUILD_YEAR_DEF,
                                              `SCM_GLB_BUILD_MONTH_DEF,
                                              `SCM_GLB_BUILD_DAY_DEF,
                                              `SCM_GLB_BUILD_HOUR_DEF,
                                              `SCM_GLB_BUILD_MINUTE_DEF };
                 end
                 `SCM_GLB_BUILD_CATEGORY_ADDR  : begin
					     avmm_readdata         <= {31'h0, `SCM_GLB_BUILD_CATEGORY_DEF};
                 end
                 `SCM_GLB_DEVICE_LOCATION_ADDR : begin
					     avmm_readdata         <= {30'h0, `SCM_GLB_DEVICE_LOCTAION_DEF};
                 end
                 `SCM_GLB_STATUS_ADDR          : begin
					     avmm_readdata         <= {31'h0, mcsi_aligned};
                 end
                 `SCM_GLB_CONTROL_ADDR         : begin
					     avmm_readdata         <= scm_global_reg[`SCM_GLB_CONTROL_ADDR / 4];
                 end
                 `SCM_GLB_SCRATCH_ADDR         : begin
					     avmm_readdata         <= scm_global_reg[`SCM_GLB_SCRATCH_ADDR / 4];
                 end	  
                 default                       : begin
					     avmm_readdata         <= 32'hFFFF_FFFF; //reture bad addr
                 end
            endcase
        end
    end
end

//readvalid
always @ ( posedge clk ) begin
    if ( reset ) begin
        avmm_readdatavalid <= 1'b0;
    end else begin
        if ( avmm_read && (&avmm_byteenable) && (!invalid_read)) begin
            avmm_readdatavalid <= 1'b1; //readvalid asserts one clock cycle after read assetted.
        end else begin
            avmm_readdatavalid <= 1'b0;
        end
    end
end

//invalid_write, use combinational logic for error report
always @ ( * ) begin
    if ( reset ) begin
		  invalid_write                  = 1'b0;
    end else begin
        if ( avmm_write && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                `SCM_GLB_CONTROL_ADDR    : begin
					    if (!(|avmm_writedata[31:2])) begin
						    invalid_write                           = 1'b0;
                   end
						 else begin
						    invalid_write                           = 1'b1;
						 end
                 end
                `SCM_GLB_SCRATCH_ADDR    : begin
						 invalid_write                              = 1'b0;
                end
                default                  : begin 
						 invalid_write                              = 1'b1;
                end
            endcase
        end
		  else begin
		     invalid_write              = 1'b0;
		  end		  
    end
end

//write registers
always @ ( posedge clk ) begin
    if ( reset ) begin
        scm_global_reg[`SCM_GLB_CONTROL_ADDR/4]               <= {30'h0, `SCM_GLB_UART_SRC_SEL_DEF};
        scm_global_reg[`SCM_GLB_SCRATCH_ADDR/4]               <= 32'h0;
    end else begin
        if ( avmm_write && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                `SCM_GLB_CONTROL_ADDR    : begin
					    if (!(|avmm_writedata[31:2])) begin
						    scm_global_reg[`SCM_GLB_CONTROL_ADDR/4] <= avmm_writedata;
						 end
						 else begin
						    illegal_write                           <= avmm_writedata;
						 end
					 end
                `SCM_GLB_SCRATCH_ADDR    : begin
					    scm_global_reg[`SCM_GLB_SCRATCH_ADDR/4]    <= avmm_writedata;
					 end
                default                  : begin
					    illegal_write                              <= avmm_writedata; //reture bad addr
					 end
            endcase
        end
    end
end


assign uart_source_sel = scm_global_reg[`SCM_GLB_CONTROL_ADDR/4][0];
assign bmc_force_update_sel = scm_global_reg[`SCM_GLB_CONTROL_ADDR/4][1];

endmodule    